package BNS.coloring;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Graphics;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Label;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.rmi.RemoteException;
import java.util.Vector;

import javax.swing.Icon;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JProgressBar;

import BNS.graphs.ColorMap;
import BNS.graphs.Graph;
import BNS.graphs.GraphPanel;
import BNS.graphs.Node;
import BNS.plumbing.BNSPersona;
import BNS.plumbing.ClientLiaison;
import BNS.plumbing.ClientLiaisonInterface;


/** The applet GUI that presents to the player of ColoringGame. */
public class ColoringPersona extends BNSPersona implements ActionListener {
	/**
   * 
   */
  private static final long serialVersionUID = 1L;
  private ClientLiaisonInterface game;
	private Graph graphlet;
	private JButton[] colorButton;
	private JComponent panel;
	private GraphPanel graphPanel;
	private int chroma, timeLimit, numEdges;
	private JProgressBar progressBar, timeBar;
	private JLabel messageBar, dynamicLabel;
	private PatchIcon winIcon;
	private final ColorMap order,render;
	private boolean showNodes;
	private final String payoffDefinition;//, payoffDefinition2;
	private final Vector<Float> bonus;
	//private final int specialColor;
	
	/** This constructor just calls its superclass's constructor, with all
		the same parameters. It is intended to be called on the *server machine*,
		so that the 5 arguments here are stored in it; later the resulting object 
		is passed to the client where further setup is performed.

		@param g the (remote) game being played
		@param id the clientID
		@param gr the graphlet for this player
		@param cr the number of colors to be used in this game
		@param tl the maximum number of seconds this game will be allowed to run
		@param ne the total number of edges in the master graph
		@param colormapIsIdentity flag indicating whether to use true colors to decorate nodes;
			if false, will use randomized colors instead
		@param showNodes flag indicating whether to hide the node colors until a move is made
		@param defn the definition of rewards for success and failure
		@param payOffStr an array of formatted dollar amounts for the various outcomes (yuck!)
		*/
	public ColoringPersona(ClientLiaison g, Graph gr, int cr, int tl, int ne, 
						   boolean colormapIsIdentity, boolean showNodes, 
						   String defn, String[] payOffStr, Vector<Float> bo) { 
		game=g; graphlet=gr; chroma= cr; timeLimit=tl; numEdges=ne; 
		order= colormapIsIdentity? ColorMap.identity():ColorMap.scramble(chroma);
		render= colormapIsIdentity? ColorMap.identity()
								  : ColorMap.scramble(GraphPanel.swatches.length-1);
		this.showNodes= showNodes;
		bonus=bo;
    //specialColor=sp;
    payoffDefinition= defn;
		/*payoffDefinition2= specialColor<=0?null:
		  GraphPanel.swatches[render.map(sp)].name +
      " you will earn "+payOffStr[1]+", otherwise you will earn "+payOffStr[0]+".";
		*/}
		
	/** returns the Node that is the eigennode for this graphlet.
		If the graph is the mastergraph, then this returns null.
		*/
	public Node getEigenNode() { return graphlet.eigenNode; }
	
	public JComponent startPanel() {
		panel= new JPanel();		
		panel.setLayout(new BorderLayout());
//		panel.setLayout(new BoxLayout(panel,BoxLayout.Y_AXIS));
		
		JPanel statusPanel= new JPanel();
		GridBagLayout gridbag= new GridBagLayout();
		GridBagConstraints constraints= new GridBagConstraints();
		
		//g.setFont(new Font("SansSerif", Font.PLAIN, 14));
		statusPanel.setLayout(gridbag);
		
		constraints.fill= GridBagConstraints.BOTH;
		constraints.weightx= 1.0;
		JLabel label1= new JLabel("progress:",JLabel.RIGHT);
		gridbag.setConstraints(label1, constraints);
		statusPanel.add(label1);
		progressBar= new JProgressBar(0,numEdges);
		progressBar.setValue(0);
		progressBar.setStringPainted(true);
		progressBar.setAlignmentX(Component.CENTER_ALIGNMENT);
		constraints.gridwidth= GridBagConstraints.REMAINDER; //end row
		gridbag.setConstraints(progressBar, constraints);
		statusPanel.add(progressBar);
		
		JLabel label2= new JLabel("status:",JLabel.RIGHT);
		constraints.gridwidth= GridBagConstraints.RELATIVE;
		gridbag.setConstraints(label2, constraints);
		statusPanel.add(label2);
		messageBar= new JLabel("persona born",JLabel.CENTER);
		messageBar.setAlignmentX(Component.CENTER_ALIGNMENT);
		constraints.gridwidth= GridBagConstraints.REMAINDER; //end row
		gridbag.setConstraints(messageBar, constraints);
		statusPanel.add(messageBar);
		
		JLabel label3= new JLabel("elapsed time:",JLabel.RIGHT);
		constraints.gridwidth= GridBagConstraints.RELATIVE;
		gridbag.setConstraints(label3, constraints);
		statusPanel.add(label3);
		timeBar= new JProgressBar(0, timeLimit*1000);
		timeBar.setValue(0);
		timeBar.setAlignmentX(Component.CENTER_ALIGNMENT);
		constraints.gridwidth= GridBagConstraints.REMAINDER; //end row
		gridbag.setConstraints(timeBar, constraints);
		statusPanel.add(timeBar);
		
/*	This section was used in games with indepedent payoffs	
		JLabel label4= new JLabel("your current payoff:",JLabel.RIGHT);
		constraints.gridwidth= GridBagConstraints.RELATIVE;
		gridbag.setConstraints(label4, constraints);
		statusPanel.add(label4);
		dynamicLabel = new JLabel(winIcon);
		constraints.gridwidth= GridBagConstraints.REMAINDER; //end row
		gridbag.setConstraints(dynamicLabel, constraints);
		statusPanel.add(dynamicLabel);
*/		
    JLabel newl1= new JLabel(" ",JLabel.CENTER);
    gridbag.setConstraints(newl1, constraints);
    statusPanel.add(newl1);
    
    JLabel defnBar= new JLabel(payoffDefinition,JLabel.CENTER);
    gridbag.setConstraints(defnBar, constraints);
    statusPanel.add(defnBar);   
    
    if(bonus.size()>0) {
      JLabel newl= new JLabel(" ",JLabel.CENTER);
      gridbag.setConstraints(newl, constraints);
      statusPanel.add(newl);
      
      JLabel defn2Bar= new JLabel("Additionally, you will earn a bonus for " +
      		"the color you have chosen when the session ends successfully.",JLabel.CENTER);
      gridbag.setConstraints(defn2Bar, constraints);
      statusPanel.add(defn2Bar);
      
      JLabel defn3Bar= new JLabel("The bonuses are listed below.",JLabel.CENTER);
      gridbag.setConstraints(defn3Bar, constraints);
      statusPanel.add(defn3Bar);
      
      JLabel newl2= new JLabel(" ",JLabel.CENTER);
      gridbag.setConstraints(newl2, constraints);
      statusPanel.add(newl2);   
      
      //JLabel payOffTable;
      //String row;
      for(int i=0;i<chroma;i++) {
        
        statusPanel.add(new JLabel(GraphPanel.swatches[render.map(i+1)].name+": "+
                String.format("$%.2f",bonus.size()>i?bonus.get(i):0)));
      }
    }
    
		panel.add("North", statusPanel);
		statusPanel.setMaximumSize(new Dimension(450,400));	// ANY limit seems to tame this
		
		graphPanel= new GraphPanel(render);
		graphPanel.setDisplayNodes(showNodes);
//		graphPanel.setSize(500,200);
//		graphPanel.setMinimumSize(new Dimension(500,500));
		panel.add("Center",graphPanel);
		graphPanel.init(graphlet);
		
		JPanel colorPanel= new JPanel(new FlowLayout());
		colorPanel.add(new Label("your color:"));
		colorButton= new JButton[chroma];
		for (int i=0; i<chroma; i++) {
			//System.err.println("startPanel "+ i +" "+GraphPanel.swatches[i+1].name);
			colorButton[i]= new JButton(GraphPanel.swatches[render.map(order.map(i+1))].name);
//			colorButton[i].setForeground(GraphPanel.swatches[render.map(order.map(i+1))].color);
			colorButton[i].setBackground(GraphPanel.swatches[render.map(order.map(i+1))].color);
			colorButton[i].addActionListener(this);
			colorPanel.add(colorButton[i]);
			}
//		colorPanel.setMaximumSize(new Dimension(500,400));	// ANY limit seems to overconstrain this
//		colorPanel.setMinimumSize(new Dimension(500,200));
		colorPanel.setPreferredSize(new Dimension(500,chroma>5?75:45));
		panel.add("South", colorPanel);

		return panel;
		}
	
	public JComponent killPanel() { return panel; }
	
	/** mandated by ActionListener; handles coloring buttons */
	public void actionPerformed(ActionEvent e) {
		Object src= e.getSource();
		//System.err.println("actionPerformed "+ src +"{"+bYEL+","+bRED+","+bGRN+"}");
		int but= 0; while (src!=colorButton[but]) but++;
		try { 
			if(game.alter(new Integer(order.map(but+1)))) 
				graphPanel.setDisplayNodes(true);
			}
		catch(RemoteException ex) { System.err.println(ex); }
		}
	
	public void gameUpdate(java.io.Serializable dataBlob) {
		assert (dataBlob instanceof BNS.coloring.Envelope);
		BNS.coloring.Envelope envelope= (BNS.coloring.Envelope)dataBlob;
		setMessage(envelope.status);
//		statusPanel.setWinStatus(envelope.localStates.winStatus);
		timerUpdate(envelope.elapsedTime);
		progressUpdate(envelope.currentProgress);
//		statusPanel.setPay(envelope.local.payRate, envelope.local.cumulativePay);

/*		int[] state= (int[])dataBlob;
		//System.err.print("client "+ myID +" gameUpdate: ");
		Enumeration eno= graphlet.enumNodes();
		while (eno.hasMoreElements()) {
			Node n= (Node)eno.nextElement();
			int nidx= n.getIndex();
			n.color= nhbrs[nidx];
			//System.err.print(" "+ n.lbl +" ["+ nidx +"]  value "+ n.color);
			}*/
		NodeState[] nhbrs= envelope.localStates;
		for (int nsi=0; nsi<nhbrs.length; nsi++) {
			Node n= graphlet.getNode(nhbrs[nsi].index);
			n.color= nhbrs[nsi].colorInt;
			}
		//System.err.println();
		graphPanel.repaint();
		}
	
	/** called to update the display of elapsed game time */
	public void timerUpdate(long elapsedTime) { timeBar.setValue((int)elapsedTime);	}
	
	/** called to update the display of current overall "social welfare" */
	public void progressUpdate(int progress) { progressBar.setValue(progress); }
	
	/** called to update the display of an incidental status message to player */
	public void setMessage(String message) { 
		if (message==null) return;
		messageBar.setText(message);
		Toolkit.getDefaultToolkit().beep();
		}
	
	/** called to update the indicator of local well-coloredness */
	public void setWinStatus(int status) { winIcon.setStatus(status); dynamicLabel.repaint(); }
	
	public int getCycleTime() { return 200; }
	
	public String getTitle() { return "ColoringPersona 1 (client "+game+")"; }
	
	/** A little icon class that displays the current winStatus value. */
	class PatchIcon implements Icon,java.io.Serializable {
		/**
     * 
     */
    private static final long serialVersionUID = 1L;
    private int wide, nPatches, patch, strhigh, status;
		private final String[] lbls;
		public int getIconWidth() { return wide; }
		public int getIconHeight() { return 18; }
		public PatchIcon(int wd, int fontHigh, String[] lbls) {
			wide=wd; this.lbls=lbls; nPatches=lbls.length; patch=wide/nPatches;
			strhigh= (fontHigh+getIconHeight())/2;
			}
		public void setStatus(int stat) { status=stat; }
		
		public void paintIcon(Component c, Graphics g, int x, int y) {
			int horz= x + patch* status;
				//System.err.println("paintIcon "+horz+","+ y);
			g.fill3DRect(horz, y, patch, getIconHeight(), true);
			g.setColor(Color.WHITE);
			int strwide= g.getFontMetrics().stringWidth(lbls[status]);
			g.drawString(lbls[status],horz+(patch-strwide)/2,y+strhigh);
			}
		};
		
	} // EOC ColoringPersona
